﻿Imports System
Imports System.Collections.Generic
Imports System.Text
Imports System.Xml.Serialization
Imports System.Reflection
Imports System.Security.Cryptography
Imports System.Xml

' Code converted from http://www.leastprivilege.com/ProtectedXml.aspx 
Namespace LeastPrivilege.Tools.ProtectedXml
    Public MustInherit Class ProtectedXmlBase
        Implements IXmlSerializable
        Private scope As System.Security.Cryptography.DataProtectionScope

#Region "IXmlSerializable Members"

        Private Function GetSchema() As System.Xml.Schema.XmlSchema Implements IXmlSerializable.GetSchema
            Throw New Exception("The method or operation is not implemented.")
        End Function

        Private Sub ReadXml(ByVal reader As System.Xml.XmlReader) Implements IXmlSerializable.ReadXml
            Dim t As Type = Me.[GetType]()
            scope = getScope(t)

            While reader.Read()
                Select Case reader.NodeType
                    Case XmlNodeType.Element
                        setField(t, reader.Name, reader.ReadString())
                        Exit Select
                End Select
            End While
        End Sub

        Private Sub WriteXml(ByVal writer As System.Xml.XmlWriter) Implements IXmlSerializable.WriteXml
            Dim t As Type = Me.[GetType]()
            scope = getScope(t)

            Dim fields As FieldInfo() = t.GetFields(BindingFlags.[Public] Or BindingFlags.Instance)
            For Each f As FieldInfo In fields
                If isProtected(f) Then
                    writer.WriteElementString(f.Name, encrypt(f.GetValue(Me).ToString()))
                Else
                    writer.WriteElementString(f.Name, f.GetValue(Me).ToString())
                End If
            Next
        End Sub

#End Region

#Region "Helpers"

        Private Function getScope(ByVal t As Type) As DataProtectionScope
            Dim pXmlAttr As ProtectedXmlAttribute() = DirectCast(t.GetCustomAttributes(GetType(ProtectedXmlAttribute), False), ProtectedXmlAttribute())

            If pXmlAttr.Length > 0 Then
                Return pXmlAttr(0).Scope
            Else
                Throw New InvalidOperationException("You have to set a Scope with [ProtectedXml]")
            End If

        End Function

        Private Sub setField(ByVal t As Type, ByVal name As String, ByVal value As String)
            Dim f As FieldInfo = t.GetField(name)

            If isProtected(f) Then
                f.SetValue(Me, Convert.ChangeType(decrypt(value), f.FieldType))
            Else
                f.SetValue(Me, Convert.ChangeType(value, f.FieldType))
            End If
        End Sub

        Private Function encrypt(ByVal text As String) As String
            Dim textBytes As Byte() = System.Text.Encoding.ASCII.GetBytes(text)
            Dim encBytes As Byte() = ProtectedData.Protect(textBytes, Nothing, scope)

            Return Convert.ToBase64String(encBytes)
        End Function

        Private Function decrypt(ByVal text As String) As String
            Dim encBytes As Byte() = Convert.FromBase64String(text)
            Dim textBytes As Byte() = ProtectedData.Unprotect(encBytes, Nothing, scope)

            Return System.Text.Encoding.ASCII.GetString(textBytes)
        End Function

        Private Function isProtected(ByVal f As FieldInfo) As Boolean
            Dim attrs As Object() = f.GetCustomAttributes(GetType(ProtectedFieldAttribute), False)
            If attrs.Length > 0 Then
                Return True
            Else
                Return False
            End If
        End Function

#End Region

    End Class

#Region "ProtextedXMLFields"


    <AttributeUsage(AttributeTargets.Field)> _
    Public Class ProtectedFieldAttribute
        Inherits Attribute
    End Class
#End Region

#Region "ProtextedXMLFields"

    <AttributeUsage(AttributeTargets.[Class])> _
    Public Class ProtectedXmlAttribute
        Inherits Attribute
        Public Scope As DataProtectionScope

        Public Sub New(ByVal scope__1 As DataProtectionScope)
            Scope = scope__1
        End Sub
    End Class
#End Region



End Namespace

